'''
Created on Mar 23, 2013

@author: Petko
'''
from django.views.generic.base import View
from sent.utils import createValidToken, CommonResult, ErrorIds, AccessToken
from sent.utils.result_printer import getPrinter
from sent.model.OpinionDBManager import OpinionDBManager
from sent.model.entities.opinion import OpinionEvaluations, Opinion, OpinionState, PendingUpdateState
from sent.opinion import extract_path_to_base, change_att_deltas_to_base,\
    neutralize_non_neutral_opinion
from sent.utils.attention import Deltas, AttentionDelta
from sententialyzer.settings import DEBUG
import time
from sent.utils.commit_scheduler import g_commit_scheduler

class UpdateOpinion(View):
    '''
    classdocs
    '''

    def __init__(self):
        '''
        Constructor
        '''
        pass
    
    def post(self, *args, **kwargs):
        accessTokenStr = self.request.POST.get('access_token', None)
        opinionId = self.request.POST.get('opinion_id', None)
        title = self.request.POST.get('title', None)
        content = self.request.POST.get('content', None)
        opinion_data = self.request.POST.get('opinion_data', None)
        evaluation = self.request.POST.get('evaluation', '')
        include_path_to_base = self.request.POST.get('include_path_to_base', False)
        include_path_to_base = include_path_to_base and include_path_to_base != "0"
        
        commit_delay=0
        if DEBUG:
            commit_delay = float(self.request.POST.get('commit_delay', 0))
            
        common_res = CommonResult()
        
        new_eval = OpinionEvaluations.STRING_TO_EVAL.get(evaluation, 0)
        
        if accessTokenStr and opinionId:
            
            accessToken, error = createValidToken(accessTokenStr)
            if accessToken:
                assert isinstance(accessToken, AccessToken)
                dbman = OpinionDBManager(False)
                try:
                    if new_eval:
                        #lock the user so that the user cannot create 2 votes via concurrent queries
                        dbman.lock_user(accessToken.getOwnerUUID())
                    
                    #lock in shared mode to synchronize with the commit of hte pending operations
                    op = dbman.get_opinion_by_id_str(opinionId, lockmode='read',
                                                     allowed_states=[OpinionState.NORMAL, OpinionState.WAITING_UPDATE, OpinionState.INSERTED])
                    if op:
                        if op.getCreatorUUID() == accessToken.getOwnerUUID():
                            assert isinstance(op, Opinion)
                            deltas = Deltas()
                            try:
                                base_op_id = op.base_op_id
                                pending_ud = dbman.lock_base_op_ro(base_op_id)
                                
                                if pending_ud:
                                    dbman.expunge(pending_ud)
                                
                                #so that the opinion data is really refreshed
                                #dbman.expunge(op)
                                
                                #Read again and lock in read mode so that the latest version of the opinion is received
                                #op = dbman.get_opinion_by_id_str(opinionId, lockmode='read')
                                #op = dbman.get_opinion_by_id_str(opinionId)
                                
                                text_data = None
                                if opinion_data is not None \
                                    or (title is not None and op.has_content) \
                                    or (content is not None and op.has_content):
                                    
                                    text_data = dbman.getOpinionTextData(opinionId)
                                    
                                    if opinion_data is not None:
                                        text_data.external_data = opinion_data
                                        op.external_data = opinion_data
                                    
                                    #change the title or content only if the opinion is created with content 
                                    if op.has_content:
                                        if title is not None:
                                            text_data.title = title
                                            op.title = title
                                            
                                        if content is not None:
                                            text_data.content = content
                                            op.textData = content
                                
                                expected_eval = op.evaluation
                                if new_eval:
                                    expected_eval = new_eval
                                    
                                if len(op.title) == 0 and len(op.textData) == 0 and expected_eval == OpinionEvaluations.NEUTRAL:
                                    common_res.errorId = ErrorIds.BADREQUEST
                                    common_res.result = "Cannot update opinion to be neutral without content. Please use Delete Opinion in this case"
                                else :
                                    common_res.result = {}
                                    common_res.result['impact'] = {}
                                                
                                    prev_eval = op.evaluation
                                    if new_eval and prev_eval != new_eval:
                                        
                                        non_neutral_op = None
                                        
                                        if new_eval != OpinionEvaluations.NEUTRAL and prev_eval == OpinionEvaluations.NEUTRAL:
                                            #if currently the opinion is not neutral, no need to search of another non-neutral opinion
                                            non_neutral_op = dbman.get_non_neutral_op_per_usr_bop(op.creator_id, op.base_op_id)
                                        
                                        opinions_to_update = dbman.get_path_to_opinions_safe(True, op, non_neutral_op)
                                        
                                        impact = neutralize_non_neutral_opinion(non_neutral_op, opinions_to_update, deltas, dbman)
                                        common_res.result['impact'].update(impact)
                                        
                                        path_found, path_to_op = extract_path_to_base(op, opinions_to_update)
                                        
                                        assert path_found
                                        
                                        op_positive_attention = op.positive_attention
                                        op_negative_attention = op.negative_attention
                                        
                                        current_op_delta = deltas.getDelta(op.id)
                                        if current_op_delta:
                                            #The non-neutral opinion may have been a child of the updated opinion.
                                            #In that case - apply the delta to the current opinion before the
                                            #next operations
                                            assert isinstance(current_op_delta, AttentionDelta)
                                            op_positive_attention += current_op_delta.positive_attention_delta
                                            op_negative_attention += current_op_delta.negative_attention_delta
                                        
                                        #Neutralize op
                                        if prev_eval == OpinionEvaluations.POSITIVE:
                                            change_att_deltas_to_base(deltas, path_to_op, -op_positive_attention - 1, -op_negative_attention)
                                        elif prev_eval == OpinionEvaluations.NEGATIVE:
                                            change_att_deltas_to_base(deltas, path_to_op, -op_negative_attention, -op_positive_attention - 1)
                                        
                                        #Now apply the new evaluation (if any)
                                        if new_eval == OpinionEvaluations.POSITIVE:
                                            change_att_deltas_to_base(deltas, path_to_op, op_positive_attention + 1, op_negative_attention)
                                        elif new_eval == OpinionEvaluations.NEGATIVE:
                                            change_att_deltas_to_base(deltas, path_to_op, op_negative_attention, op_positive_attention + 1)
                                        
                                        if include_path_to_base:
                                            common_res.result['path_to_base'] = [pathOp.toPrimitiveDict() for pathOp in path_to_op]
                                        
                                        if op.op_state == OpinionState.NORMAL:
                                            op.prev_eval = prev_eval
                                        op.evaluation = new_eval
                                    else:
                                        new_eval = prev_eval
                                    
                                    deltas_list = [delta for delta in deltas.getAllDeltas()]
                                    #dbman.apply_attention_deltas(deltas_list)
                                    
                                    if text_data:
                                        #prev_has_content = op.has_content
                                        #op.has_content = len(op.title) != 0 or len(op.textData) != 0 or op.getTotalSubopinions() > 0
                                        dbman.update_op_text_data(text_data)
                                    
                                    if prev_eval != new_eval:
                                        if op.op_state != OpinionState.INSERTED:
                                            op.op_state = OpinionState.WAITING_UPDATE
                                        dbman.update_opinion(op)
                                    
                                    #if prev_eval != new_eval or prev_has_content != op.has_content:
#                                        dbman.update_parent_stats(op.parent_id, 
#                                                                int(op.has_content) - int(prev_has_content),
#                                                                (new_eval == OpinionEvaluations.POSITIVE) - (prev_eval == OpinionEvaluations.POSITIVE),
#                                                                (new_eval == OpinionEvaluations.NEUTRAL) - (prev_eval == OpinionEvaluations.NEUTRAL),
#                                                                (new_eval == OpinionEvaluations.NEGATIVE) - (prev_eval == OpinionEvaluations.NEGATIVE))
                                    
                                    common_res.result['opinion'] = op.toPrimitiveDict()
                                    common_res.result['impact']['attention_deltas'] = [delta.toPrimitiveDict() for delta in deltas_list]
                                    
                                    if commit_delay>0:
                                        time.sleep(commit_delay)
                                        
                                    dbman.commitSession()
                                    
                                    if prev_eval != new_eval \
                                        and (not pending_ud or pending_ud.state == PendingUpdateState.IDLE):
                                        
                                        dbman.set_base_op_for_update(base_op_id)
                                        dbman.commitSession()
                                        
                                        g_commit_scheduler.schedule_commit()
                                        
                                    common_res.errorId = ErrorIds.SUCCESS
                            except:
                                dbman.rollbackSession()
                                raise
                        else:
                            common_res.errorId = ErrorIds.UNAUTHORIZED
                            common_res.result = "Need to be owner of the opinion to update it"
                    else:
                        common_res.errorId = ErrorIds.NOT_FOUND
                        common_res.result = "Opinion %s not found" % opinionId
                finally:
                    dbman.closeSession()
            else:
                common_res.errorId = ErrorIds.UNAUTHORIZED
                common_res.result = error
        else:
            common_res.errorId = ErrorIds.BADREQUEST
            common_res.result = "Missing parameter"
            
        return getPrinter().printResult(common_res)
    
    